home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Crossword / Source / Dictionary.m < prev    next >
Text File  |  1995-06-12  |  8KB  |  419 lines

  1. /*
  2.  
  3. File Dictionary.m
  4.  
  5. A dictionary is responsible for finding words that match a prespecified pattern.  Each dictionary is tied to a .xdict file that contains both words and index information to facilitate lookup.  See the file xdict.c for information about the dictionary file format.
  6.  
  7. */
  8.  
  9. #import <appkit/appkit.h>
  10.  
  11. #import "Dictionary.h"
  12. #import "FunctionCache.h"
  13. #import "filing.h"
  14.  
  15. #import <streams/streams.h>
  16. #import <stdio.h>
  17. #import <strings.h>
  18. #import <ctype.h>
  19. #import <libc.h>
  20.  
  21.  
  22. /* ————————————————————————————————————————————————————————————————————————————  */
  23.  
  24.  
  25. static char *    types [] = {
  26.  
  27.         "xdict",
  28.         "XDICT",
  29.         NULL
  30.     };
  31.  
  32.  
  33. #define INITIALCOUNT        0
  34. #define ENTRYSIZE(n)        ((sizeof(char) + sizeof(long)) * n)
  35. #define OFFSETSIZE(i,n)        (sizeof(char) * n + sizeof(long) * i)
  36.  
  37. #define CACHESIZE        3000
  38.  
  39.  
  40. /* ————————————————————————————————————————————————————————————————————————————  */
  41.  
  42.  
  43. // static void        lowercase            (char *);
  44. static void        createDictionary    (NXStream * input, NXStream * output);
  45. static void        openDictionary        (NXStream * input, wordIndex index);
  46. static void        createIndex            (NXStream *, NXStream *, wordIndex, int);
  47. static int        getWord                (NXStream *, char *, int);
  48. static void     getAppDirectory     (char *);
  49.  
  50.  
  51. /* ————————————————————————————————————————————————————————————————————————————  */
  52.  
  53.  
  54. @implementation Dictionary
  55.  
  56. - (wordIndex *)    getIndex     {    return &index;    }
  57.  
  58.  
  59. /* ————————————————————————————————————————————————————————————————————————————  */
  60.  
  61.  
  62. - init
  63. {
  64.     char    directory [1000];
  65.     
  66.     [super  init];
  67.     
  68.     cache = [[FunctionCache  alloc]
  69.     
  70.                 initKeyType: ISASTRING 
  71.                 valueType: ISAOBJECT
  72.                 capacity: CACHESIZE ];
  73.     
  74.     getAppDirectory(directory);
  75.     strcat(directory, DEFAULTDICTIONARY);
  76.     [self  useDictionary: directory];
  77.     
  78.     return self;
  79. }
  80.  
  81.  
  82. - free
  83. {
  84.     [self  notify: WILLFREE];
  85.     if (file != NULL) NXClose(file);
  86.     [super  free];
  87.     
  88.     return self;
  89. }
  90.  
  91.  
  92. - create: sender
  93. {
  94.     const char    * infile, * outfile;
  95.     NXStream    * input, * output;
  96.     
  97.     if ((infile = fileForOpen(NULL)) != NULL)
  98.     if ((outfile = fileForSave("xdict")) != NULL)
  99.     {
  100.         input = NXMapFile(infile, NX_READONLY);
  101.         output = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  102.         createDictionary(input, output);
  103.         
  104.         NXSaveToFile(output, outfile);
  105.         NXClose(input);
  106.          NXCloseMemory(output, NX_FREEBUFFER);
  107.     }
  108.     
  109.     return self;
  110. }
  111.  
  112.  
  113. - use: sender
  114. {
  115.     const char    * filename;
  116.     
  117.     if ((filename = fileForOpen(types)) != NULL) [self  useDictionary: filename];
  118.     return self;
  119. }
  120.  
  121.  
  122. - useDictionary: (const char *) filename
  123. {
  124.     if (file != NULL) NXClose(file);
  125.     openDictionary(file = NXMapFile(filename, NX_READONLY), index);
  126.     [self  notify: DIDCHANGE];
  127.     
  128.     return self;
  129. }
  130.  
  131.  
  132. /* ————————————————————————————————————————————————————————————————————————————  */
  133.  
  134.  
  135. /*
  136.  
  137. Here are two methods to locate words that match a specific pattern.  The find method accepts general patterns.  Limit restricts its search to a list of previous matches and finds all words that have a given letter in a given location.
  138.  
  139. */
  140.  
  141.  
  142. - find: (char *) pattern
  143. {
  144.     id        words;
  145.     
  146.     if ((words = [cache  find: pattern]) == nil)
  147.                 words = [self  findWithoutCache: pattern];
  148.     
  149.     return words;
  150. }
  151.  
  152.  
  153. - find: (char *) pattern  changeAt: (int) i
  154. {
  155.     id        words;
  156.     char    c;
  157.     int        n;
  158.     
  159.     if ((words = [cache  find: pattern]) == nil)
  160.     {
  161.         if ((c = pattern[i]) != WILDCARD)
  162.         {
  163.             n = strlen(pattern);
  164.             pattern[i] = WILDCARD;
  165.             words = [self  find: pattern];
  166.             words = [self  limit: words  toLetter: c - 'a'  at: i  forLength: n ];
  167.             pattern[i] = c;
  168.         }
  169.         
  170.         else words = [self  findWithoutCache: pattern];
  171.     }
  172.     
  173.     return words;
  174. }
  175.  
  176.  
  177. - findWithoutCache: (char *) pattern
  178. {
  179.     char    * string;
  180.     id        words, match;
  181.     int        i, n;
  182.     
  183.     words = match = nil;
  184.     i = n = strlen(pattern);
  185.     
  186.     while (i--) if (pattern[i] != WILDCARD)
  187.     {
  188.         match = words;
  189.         words = [self limit: match  toLetter: pattern[i] - 'a' at: i forLength: n];
  190.         [match  free];
  191.     }
  192.     
  193.     string = (char *) malloc(strlen(pattern) + 1);
  194.     strcpy(string, pattern);
  195.     [cache  add: string  value: words];
  196.     
  197.     return words;
  198. }
  199.  
  200.  
  201. - limit: match  toLetter: (char) c  at: (int) i  forLength: (int) n
  202. {
  203.     int        current, matchWord;
  204.     long    size, offset;
  205.     int        j, count;
  206.     id        words;
  207.     
  208.     if (n < MINLETTERS) return nil;
  209.     
  210.     matchWord = j = count = 0;
  211.     words = [[Storage  alloc]
  212.                 initCount: 0  elementSize: sizeof(long)  description: "i" ];
  213.     
  214.     if (match != nil)
  215.     {
  216.         if ((count = [match  count]) == 0) return words;
  217.         matchWord = WORD(match, j = 0);
  218.     }
  219.     
  220.     size = ENTRYSIZE(n);
  221.     offset = index[n].words + OFFSETSIZE(i, n);
  222.     current = index[n].linkTable[c][i].first;
  223.     
  224.     while (current != LAST)
  225.     {
  226.         if (match == nil) [words  addElement: ¤t];
  227.         else
  228.         {
  229.             while (matchWord > current)
  230.                 if (++j < count)
  231.                     
  232.                     matchWord = WORD(match, j);        else
  233.                     break;
  234.             
  235.             if (j == count) return words;
  236.             if (matchWord == current) [words  addElement: ¤t];
  237.         }
  238.         
  239.         NXSeek(file, size * current + offset, NX_FROMSTART);
  240.         NXRead(file, ¤t, sizeof(int));
  241.     }
  242.     
  243.     return words;
  244. }
  245.  
  246.  
  247. - read: (char *) string  word: (int) i  forLength: (int) n
  248. {
  249.     NXSeek(file, ENTRYSIZE(n) * i + index[n].words, NX_FROMSTART);
  250.     NXRead(file, string, n);
  251.     string[n] = '\0';
  252.     
  253.     return self;
  254. }
  255.  
  256.  
  257. @end
  258.  
  259.  
  260. /* ————————————————————————————————————————————————————————————————————————————  */
  261.  
  262.  
  263. /*
  264.  
  265. static void        lowercase    (string)
  266.  
  267. char        * string;
  268. {
  269.     while (*string != '\0')
  270.     {
  271.         *string = tolower(*string);
  272.         string++;
  273.     }
  274. }
  275.  
  276. */
  277.  
  278.  
  279. /* ————————————————————————————————————————————————————————————————————————————  */
  280.  
  281.  
  282. /*
  283.  
  284. Here are the routines that create and open dictionary files.
  285.  
  286. */
  287.  
  288. static void    createDictionary (input, output)
  289.  
  290. NXStream    * input, * output;
  291.  
  292. {
  293.     wordIndex    index;
  294.     int            n;
  295.     
  296.     NXSeek(output, sizeof(wordIndex), NX_FROMSTART);
  297.     
  298.     for (n = MINLETTERS; n <= MAXLETTERS; n++)
  299.     {
  300.         NXSeek(input, 0, NX_FROMSTART);
  301.         createIndex(input, output, index, n);
  302.     }
  303.     
  304.     NXSeek(output, 0, NX_FROMSTART);
  305.     NXWrite(output, index, sizeof(wordIndex));
  306. }
  307.  
  308.  
  309. static void    openDictionary (input, index)
  310.  
  311. NXStream    * input;
  312. wordIndex    index;
  313.  
  314. {
  315.     NXSeek(input, 0, NX_FROMSTART);
  316.     NXRead(input, index, sizeof(wordIndex));
  317. }
  318.  
  319.  
  320. /* ————————————————————————————————————————————————————————————————————————————  */
  321.  
  322.  
  323. static void    createIndex (input, output, index, n)
  324.  
  325. NXStream    * input, * output;
  326. wordIndex    index;
  327. int            n;
  328.  
  329. {
  330.     char    word [100];
  331.     int        i, j;
  332.     
  333.     index[n].words = NXTell(output);
  334.     index[n].n = 0;
  335.     
  336.     for (j = 0; j < LETTERS; j++)
  337.     for (i = 0; i < MAXLETTERS; i++)
  338.     {
  339.         index[n].linkTable[j][i].first = LAST;
  340.         index[n].linkTable[j][i].n = 0;
  341.     }
  342.     
  343.     while (getWord(input, word, n))
  344.     {
  345.         NXWrite(output, word, n);
  346.         for (i = 0; i < n; i++)
  347.         {
  348.             j = word[i] - 'a';
  349.             NXWrite(output, &index[n].linkTable[j][i], sizeof(int));
  350.             index[n].linkTable[j][i].first = index[n].n;
  351.             index[n].linkTable[j][i].n++;
  352.         }
  353.         
  354.         index[n].n++;
  355.     }
  356. }
  357.  
  358.  
  359. static int    getWord (input, word, n)
  360.  
  361. NXStream    * input;
  362. char        * word;
  363. int            n;
  364.  
  365. {
  366.     int        i, bad;
  367.     
  368.     i = bad = 0;
  369.     while ((word[i] = NXGetc(input)) != EOF)
  370.     {
  371.         if (word[i] == '\n')
  372.         {
  373.             if (!bad && (i == n))
  374.             {
  375.                 word[i] = '\0';
  376.                 return 1;
  377.             }
  378.             
  379.             i = bad = 0;
  380.         }
  381.         
  382.         else if (islower(word[i])) i++;
  383.         else bad = 1;
  384.     }
  385.     
  386.     return 0;
  387. }
  388.  
  389.  
  390. /* ————————————————————————————————————————————————————————————————————————————  */
  391.  
  392.  
  393. /*
  394.  
  395. Here is code to get the application's directory.  The code is taken from NeXTanswers appkit #642.
  396.  
  397. */
  398.  
  399. static void getAppDirectory (char *appDirectory)
  400. {
  401.     FILE *process;
  402.     char command[256];
  403.     char *suffix;
  404.  
  405.     strcpy (appDirectory,NXArgv[0]);
  406.     if (appDirectory[0] == '/') {         /* if absolute path */
  407.         if (suffix = rindex(appDirectory,'/')) 
  408.             *suffix  = '\0';             /* remove executable name */
  409.     } else {
  410.     sprintf(command,"which '%s'\n",NXArgv[0]);
  411.     process=popen(command,"r");
  412.     fscanf(process,"%s",appDirectory);
  413.     pclose(process);
  414.     if (suffix = rindex(appDirectory,'/')) 
  415.         *suffix  = '\0';             /* remove executable name */
  416.     chdir(appDirectory);
  417.     getwd(appDirectory);
  418.     }  
  419. }